﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace game
{
    public class BillboardParticleSystem : ParticleSystem
    {
        #region Fields


        protected short maxVertices;
        protected short maxIndices;

        protected BillboardParticleVertex[] vertices;
        protected short[] indices;

        #endregion

        #region Effect
        #endregion

        #region ctor

        public BillboardParticleSystem(Scene scene, Vector3 position)
            : base(scene, position)
        {
        }

        #endregion

        #region Init

        protected override void InitParameters()
        {
            textureName = "Textures\\Explosions\\expl2";
            effectName = "Effects\\BillboardParticle";
            effectTechName = "DynamicBillboardParticleAdditiveBlending";

            Acceleration = 0.0f;
            maxParticles = 100;

            emitter = null;

            MinInitialSpeed = 0.0f;
            MaxInitialSpeed = 0.0f;
            MinInitialSize = 50;
            MaxInitialSize = 100;
            MinLifeTimeMs = 1000;
            MaxLifeTimeMs = 5000;

            color = Color.White;
        }

        protected override void InitParticle(int index, Vector3 position, float initialTimeMsOffset)
        {
            float iniTime = elapsedSystemTimeMs + initialTimeMsOffset;
            vertices[index].InitialTimeMs = iniTime;
            vertices[index + 1].InitialTimeMs = iniTime;
            vertices[index + 2].InitialTimeMs = iniTime;
            vertices[index + 3].InitialTimeMs = iniTime;

            float offset = RandomHelper.RandomBetween(MinInitialSize, MaxInitialSize);
            vertices[index].Offset.X = -offset;
            vertices[index].Offset.Y = -offset;
            vertices[index + 1].Offset.X = offset;
            vertices[index + 1].Offset.Y = -offset;
            vertices[index + 2].Offset.X = offset;
            vertices[index + 2].Offset.Y = offset;
            vertices[index + 3].Offset.X = -offset;
            vertices[index + 3].Offset.Y = offset;

            float dieTime = RandomHelper.RandomBetween(MinLifeTimeMs, MaxLifeTimeMs);
            vertices[index].DieTimeMs = dieTime;
            vertices[index + 1].DieTimeMs = dieTime;
            vertices[index + 2].DieTimeMs = dieTime;
            vertices[index + 3].DieTimeMs = dieTime;

            vertices[index].InitialColor = color;
            vertices[index + 1].InitialColor = color;
            vertices[index + 2].InitialColor = color;
            vertices[index + 3].InitialColor = color;

            vertices[index].TexCoord = new Vector2(1, 1);
            vertices[index + 1].TexCoord = new Vector2(0, 1);
            vertices[index + 2].TexCoord = new Vector2(0, 0);
            vertices[index + 3].TexCoord = new Vector2(1, 0);
        }

        protected override void DeinitPaticle(int index)
        {
            vertices[index].DieTimeMs = 0;
            vertices[index + 1].DieTimeMs = 0;
            vertices[index + 2].DieTimeMs = 0;
            vertices[index + 3].DieTimeMs = 0;
        }

        public override void Initialize()
        {
            maxVertices = (short)(maxParticles * 4);
            maxIndices = (short)(maxParticles * 6);
            vertices = new BillboardParticleVertex[maxVertices];
            indices = new short[maxIndices];

            for (int i = 0; i < maxParticles; i++)
            {
                DeinitPaticle(i*4);
            }

            for (int i = 0; i < maxParticles; i++)
            {
                indices[i * 6 + 0] = (short)(i * 4 + 0);
                indices[i * 6 + 1] = (short)(i * 4 + 1);
                indices[i * 6 + 2] = (short)(i * 4 + 2);

                indices[i * 6 + 3] = (short)(i * 4 + 0);
                indices[i * 6 + 4] = (short)(i * 4 + 2);
                indices[i * 6 + 5] = (short)(i * 4 + 3);
            }

            texture = ShooterGame.Instance.Content.Load<Texture>(textureName);
            effect = ShooterGame.Instance.Content.Load<Effect>(effectName);
            effectTechnique = effect.Techniques[effectTechName];

            SetEffectParamHandles();
        }

        #endregion

        #region Adding particles

        // TODO!!! COPIED FROM POINTSPRITES CLASS!!!
        public override int AddParticles(int count)
        {
            int a = count;
            while (count > 0)
            {
                if (firstFreeParticleIndex == firstActiveParticleIndex)
                {
                    break;
                }

                InitParticle(firstFreeParticleIndex, Vector3.Zero, 0);
                if (firstActiveParticleIndex == -1)
                {
                    firstActiveParticleIndex = firstFreeParticleIndex;
                }
                firstFreeParticleIndex = (firstFreeParticleIndex + 1) % maxParticles;
                count--;
            }
            return count;
        }

        public override bool AddParticle(Vector3 position, float initialTimeMsOffset)
        {
            if (firstFreeParticleIndex == firstActiveParticleIndex)
            {
                return false;
            }

            InitParticle(firstFreeParticleIndex, position, initialTimeMsOffset);

            if (firstActiveParticleIndex == -1)
            {
                firstActiveParticleIndex = firstFreeParticleIndex;
            }
            firstFreeParticleIndex = (firstFreeParticleIndex + 4) % maxVertices;

            return true;
        }

        public override void SetParticles(int n)
        {
            if (n > maxParticles)
                return;

            elapsedSystemTimeMs = 0;
            firstFreeParticleIndex = firstActiveParticleIndex = 0;
            for (int i = 0; i < n; i++)
            {
                InitParticle(i * 4, Vector3.Zero, 0);
            }
            for (int i = n; i < maxParticles; i++)
            {
                DeinitPaticle(i * 4);
            }
        }

        #endregion

        #region Update Draw

        public override void Update(GameTime gameTime)
        {
            elapsedSystemTimeMs += gameTime.ElapsedGameTime.Milliseconds;
            if (firstActiveParticleIndex != -1)
            {
                int i = 0;
                while (true)
                {
                    i++;
                    if (i > maxParticles)
                    {
                        firstActiveParticleIndex = -1;
                        break;
                    }

                    if (elapsedSystemTimeMs - vertices[firstActiveParticleIndex].InitialTimeMs
                        > vertices[firstActiveParticleIndex].DieTimeMs)
                    {
                        firstActiveParticleIndex = (firstActiveParticleIndex + 4) % maxVertices;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if (emitter != null)
                emitter.Update(gameTime, position, rotation);
        }

        public override void Draw(GameTime gameTime)
        {
            if (firstActiveParticleIndex == -1)
                return;

            SetEffectParams();

            ShooterGame.Instance.GraphicsDevice.RenderState.PointSpriteEnable = false;

            effect.CurrentTechnique = effectTechnique;

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();

                ShooterGame.Instance.GraphicsDevice.VertexDeclaration
                    = BillboardParticleVertex.VertexDecl;

                if (firstFreeParticleIndex > firstActiveParticleIndex)
                {
                    ShooterGame.Instance.GraphicsDevice.DrawUserIndexedPrimitives<BillboardParticleVertex>
                        (PrimitiveType.TriangleList,
                        vertices, firstActiveParticleIndex, firstFreeParticleIndex - firstActiveParticleIndex,
                        indices, 0, (firstFreeParticleIndex - firstActiveParticleIndex) / 2);
                }
                else
                {
                    if (firstFreeParticleIndex > 0)
                    {
                        ShooterGame.Instance.GraphicsDevice.DrawUserIndexedPrimitives<BillboardParticleVertex>
                            (PrimitiveType.TriangleList,
                            vertices, 0, firstFreeParticleIndex,
                            indices, 0, firstFreeParticleIndex / 2);
                    }

                    ShooterGame.Instance.GraphicsDevice.DrawUserIndexedPrimitives<BillboardParticleVertex>
                        (PrimitiveType.TriangleList,
                        vertices, firstActiveParticleIndex, maxVertices - firstActiveParticleIndex,
                        indices, 0, (maxVertices - firstActiveParticleIndex) / 2);
                }

                pass.End();
            }
            effect.End();
        }

        #endregion
    }
}
